package de.realriot.ipsdroid.v1.beta;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.database.sqlite.SQLiteException;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;

import de.realriot.libs.ipsjson.IPSException;
import de.realriot.libs.ipsjson.IPSjson;

public class IPSDataCache {
	
	private Handler messageHandler;
	private IPSDataHelper dh;
	private IPSjson ipshost;

	/**
	 * Constructor of this datacache class.
	 * 
	 * @param existingDataHelper The existing DataHelper object.
	 * @param existingIPSjson The existing JSON-RPC connection object.
	 * @param handler The messageHandler of the previous class.
	 */
	public IPSDataCache(IPSDataHelper existingDataHelper, IPSjson existingIPSjson, Handler handler)
	{
		dh = existingDataHelper;
		ipshost = existingIPSjson;
		messageHandler = handler;
	}

	/**
	 * Adds an array of JSONObjects to the database.
	 * 
	 * @param obj The array of JSONObjects.
	 * @throws SQLiteException
	 * @throws JSONException
	 * @throws IPSException 
	 */
	public void addToCache(JSONArray obj) throws SQLiteException, JSONException, IPSException
	{
		for ( int i = 0; i < obj.length(); i++ )
		{
			JSONObject tmpObj = obj.getJSONObject(i);
			dh.addJSONObjectToDB(tmpObj);
		}
	}
	
	/**
	 * Adds a JSONObject to the database.
	 * 
	 * @param obj The JSONObject.
	 * @throws SQLiteException 
	 * @throws JSONException
	 * @throws IPSException 
	 */
	public void addToCache(JSONObject obj) throws SQLiteException, JSONException, IPSException 
	{
		dh.addJSONObjectToDB(obj);
	}

	/**
	 * Fetches a corresponding IPS-Object of a given "normal" object. 
	 * 
	 * @param obj The JSONObject.
	 * @throws SQLiteException
	 * @throws JSONException
	 * @throws IPSException
	 */
	public JSONObject fetchCorrObject(JSONObject obj) throws SQLiteException, JSONException, IPSException 
	{
		Boolean cached = true;
		JSONObject result = new JSONObject();
		JSONObject tmpResult = new JSONObject();
		
		// Link.
		if ( obj.get("ObjectType").equals(IPSObjectType.otLink) )
		{
			result = dh.getJSONObjectFromDB("ips_links", obj.getString("ObjectID") );
			if ( result != null )
				result = dh.getJSONObjectFromDB("ips_objects", result.getString("LinkChildID") );
			
			if ( result == null )
			{
				tmpResult = ipshost.IPS("IPS_GetLink",
						new Object[] {obj.get("ObjectID")} ).getJSONObject("result");
				addToCache(tmpResult);
				
				result = ipshost.IPS("IPS_GetObject",
						new Object[] {tmpResult.get("LinkChildID")} ).getJSONObject("result");
				cached = false;
			}
		}

		// Instance.
		if ( obj.get("ObjectType").equals(IPSObjectType.otInstance) )
		{
			result = dh.getJSONObjectFromDB("ips_instances", obj.getString("ObjectID") );
			if ( result == null )
			{
				result = ipshost.IPS("IPS_GetInstance",
						new Object[] {obj.get("ObjectID")} ).getJSONObject("result");
				cached = false;
			}
		}
		
		// Variable.
		if ( obj.get("ObjectType").equals(IPSObjectType.otVariable) )
		{
			result = dh.getJSONObjectFromDB("ips_variables", obj.getString("ObjectID") );
			if ( result == null )
			{
				result = ipshost.IPS("IPS_GetVariable",
						new Object[] {obj.get("ObjectID")} ).getJSONObject("result");
				cached = false;
			}
		}
		
		// Add object to database, if we've fetched new data.
		if ( ! cached )
			addToCache(result);
		
		return result;
	}

	/**
	 * Returns a JSONArray of objects, which belong to a root object.
	 * 
	 * @param id The ID of the root object.
	 * @return Array object-childs by the root-object.
	 * @throws SQLiteException
	 * @throws JSONException
	 * @throws IPSException

	 */
	public JSONArray IPS_GetObjectChilds(int id) throws SQLiteException, JSONException, IPSException 
	{
		Boolean cached = true;
		JSONArray tmpArr = new JSONArray();
		JSONArray result = new JSONArray();
		
		// First we have to look, if the root object exists within the database.
		JSONObject obj = dh.getJSONObjectFromDB("ips_objects", String.valueOf(id));
		if ( obj != null )
		{
			JSONArray ids = obj.getJSONArray("ChildrenIDs");

			for ( int i = 0; i < ids.length(); i++ )
			{
				updateProgressDialog(obj.getString("ObjectName"), i+1, ids.length());
				JSONObject tmpObj = dh.getJSONObjectFromDB("ips_objects", ids.getString(i));
				if ( tmpObj != null )
				{
					result.put(tmpObj);
				} else
				{
					cached = false;
					continue;
				}
			}
		} else
		{
			cached = false;
		}

		// If the result cannot be fetched from cache, we have to get the informations
		// from the ip-symcon host.
		if (! cached )
		{
			tmpArr = ipshost.IPS("IPS_GetObjectChilds",
					new Object[] {id} ).getJSONArray("result");
			
			if ( tmpArr.length() > 0 )
			{
				result = tmpArr;
				
				// Add array-result to cache.
				addToCache(result);
				
				// Additionally add root-object to cache.
				addToCache(ipshost.IPS("IPS_GetObject",
						new Object[] {id}).getJSONObject("result"));
			}
		}
		return result;
	}

	/**
	 * Fetches the VariableProfile of a given Variable-Object.
	 * 
	 * @param var The IPS-Variable object.
	 * @return The corresponding variable profile to ips-variable object.
	 * @throws SQLiteException
	 * @throws JSONException
	 * @throws IPSException
	 */
	public JSONObject IPS_GetVariableProfile(JSONObject var) throws SQLiteException, JSONException, IPSException 
	{
		Boolean cached = true;
		JSONObject result = null;
		String profileName = "";
		
		if (! var.get("VariableCustomProfile").equals("") )
		{
			profileName = var.getString("VariableCustomProfile");
			result = dh.getJSONObjectFromDB("ips_varprofiles", profileName);
			
			if ( result == null )
				cached = false;
		} else if (! var.get("VariableProfile").equals("") )
		{
			profileName = var.getString("VariableProfile");
			result = dh.getJSONObjectFromDB("ips_varprofiles", profileName);
			
			if ( result == null )
				cached = false;
		}

		// Add object to database, if we've fetched new data.
		if ( ! cached && ! profileName.equals(""))
		{
			result = ipshost.IPS("IPS_GetVariableProfile",
					new Object[] {profileName} ).getJSONObject("result");
			addToCache(result);
		}
		return result;
	}
	
	/**
     * This method uses the messageHandler to update the progressDialog.
     * 
     * @param text Text to display within the dialog.
     * @param value The actual value of a progress.
     * @param maxvalue The max value of a progress.
     */
	public void updateProgressDialog(String text, int value, int maxvalue)
	{
    	Bundle data = new Bundle();
    	data.putString("state", text);
    	data.putInt("value", value);
    	data.putInt("maxvalue", maxvalue);
    	
    	Message msg = new Message();
    	msg.what = 2;
    	msg.setData(data);
    	
    	messageHandler.sendMessage(msg);
	}
}